Skip to content

fix(auth): resolve CORS errors for self-hosted deployments behind reverse proxies#4369

Merged
waleedlatif1 merged 8 commits intostagingfrom
waleedlatif1/cors-localhost-issue
May 1, 2026
Merged

fix(auth): resolve CORS errors for self-hosted deployments behind reverse proxies#4369
waleedlatif1 merged 8 commits intostagingfrom
waleedlatif1/cors-localhost-issue

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented May 1, 2026

Summary

  • Auth client uses browser origin first, falling back to NEXT_PUBLIC_APP_URL — fixes "Failed to create account" CORS errors when self-hosters' build-time env doesn't match the public origin
  • Socket client falls back to the page's origin when served from a non-localhost host, so reverse-proxied /socket.io works without setting NEXT_PUBLIC_SOCKET_URL
  • Add TRUSTED_ORIGINS env var (comma-separated) merged into Better Auth trustedOrigins for apex+www / alias domains
  • Warn at startup when NEXT_PUBLIC_APP_URL is localhost in production
  • Migrate remaining uuid / nanoid / crypto.randomUUID() usages to @sim/utils generateId / generateShortId; extend generateShortId with optional alphabet param
  • Include packages/utils in the migrations Docker image so the @sim/* workspace dep resolves
  • Document TRUSTED_ORIGINS and the new socket fallback in .env.example, docker-compose.prod.yml, and helm/sim/values.yaml

Fixes #1243

Type of Change

  • Bug fix

Testing

Tested manually. Added unit tests for getBrowserOrigin, getSocketUrl resolution order, parseOriginList, and isLocalhostUrl (14 tests). All lib/auth + lib/core/utils/urls tests passing.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…erse proxies

- auth client now uses browser origin first, falling back to NEXT_PUBLIC_APP_URL
- socket client falls back to page origin when served from non-localhost (assumes /socket.io is proxied)
- add TRUSTED_ORIGINS env var to extend Better Auth trustedOrigins (apex+www, alias hostnames)
- warn at startup when NEXT_PUBLIC_APP_URL is localhost in production
- preprocess empty NEXT_PUBLIC_SOCKET_URL so docker-compose ${VAR:-} works
- migrate remaining uuid/nanoid/randomUUID usages to @sim/utils generateId/generateShortId
- extend generateShortId with optional alphabet param (rejection sampling)
- document TRUSTED_ORIGINS in .env.example, docker-compose.prod.yml, and helm values.yaml

Fixes #1243
@vercel
Copy link
Copy Markdown

vercel Bot commented May 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment May 1, 2026 2:17am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 1, 2026

PR Summary

Medium Risk
Touches authentication origin validation and client base URL/socket URL resolution, so misconfiguration could still block sign-in/up in some deployments. Changes are well-scoped and include new unit tests for the URL/origin helpers.

Overview
Fixes self-hosted reverse-proxy deployments by preferring the runtime browser origin for auth client requests and, by default, using the page origin for Socket.IO when NEXT_PUBLIC_SOCKET_URL is unset (with a localhost fallback for dev/SSR).

Adds TRUSTED_ORIGINS (comma-separated) to extend Better Auth trustedOrigins, warns in production when NEXT_PUBLIC_APP_URL is still localhost, and updates Docker/Helm/docs to treat NEXT_PUBLIC_SOCKET_URL as optional and document the reverse-proxy /socket.io routing expectation.

Also standardizes ID generation by replacing remaining uuid/nanoid/crypto.randomUUID() usages in DB scripts and test factories with @sim/utils (generateId/generateShortId), extends generateShortId to support custom alphabets, and ensures the migrations Docker image includes packages/utils so workspace imports resolve.

Reviewed by Cursor Bugbot for commit 7b32375. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 1, 2026

Greptile Summary

This PR fixes CORS errors for self-hosted deployments by making the auth client prefer the browser's actual origin over the build-time NEXT_PUBLIC_APP_URL, adding a TRUSTED_ORIGINS env var for multi-domain setups, and falling back to the page's own origin for Socket.IO when no explicit URL is configured. It also consolidates uuid/nanoid/crypto.randomUUID() usages to the shared @sim/utils generateId/generateShortId helpers (with a new optional custom-alphabet param), fixes the migrations Docker image to include packages/utils, and updates documentation/configuration across Docker Compose, Helm, and .env.example.

Confidence Score: 5/5

Safe to merge — changes are well-tested, targeted at a real self-hosting pain point, and introduce no correctness regressions.

Only finding is a P2 nit (dead '::1' entry in a Set that is never matched by the URL API). All core logic is correct: auth client browser-origin preference, TRUSTED_ORIGINS validation, rejection-sampling in generateShortId, and socket URL fallback. 14 new unit tests pass, Docker/Helm config changes are aligned with the new defaults.

apps/sim/lib/core/utils/urls.ts (minor dead-code nit on line 106)

Important Files Changed

Filename Overview
apps/sim/lib/auth/auth-client.ts Auth client now prefers the browser's actual origin over the build-time NEXT_PUBLIC_APP_URL, fixing CORS errors for self-hosters whose build-time URL doesn't match the serving host.
apps/sim/lib/auth/auth.ts Adds TRUSTED_ORIGINS support merged into betterAuth.trustedOrigins, and a startup warning when NEXT_PUBLIC_APP_URL points to localhost in production. Clean.
apps/sim/lib/core/utils/urls.ts Introduces parseOriginList, isLocalhostUrl, getBrowserOrigin, and an enhanced getSocketUrl with origin-fallback logic. Minor dead-code nit: bare '::1' in LOCALHOST_HOSTNAMES is never matched by new URL().hostname.
apps/sim/lib/core/utils/urls.test.ts 14 new unit tests covering getBrowserOrigin, getSocketUrl resolution order, parseOriginList, and isLocalhostUrl. Good coverage including edge cases (whitespace env var, deduplication, invalid entries).
packages/utils/src/id.ts Extends generateShortId with an optional custom alphabet via rejection-sampling (nanoid's algorithm). Correct implementation: uniform distribution, no modulo bias, throws for invalid alphabet lengths.
docker/db.Dockerfile Adds packages/utils to the migrations Docker image so @sim/utils workspace dep resolves in migration scripts that now import generateId.
packages/testing/src/factories/table.factory.ts Replaces nanoid/customAlphabet with generateShortId from @sim/utils. Functionally equivalent; correctly defines the column suffix alphabet as a module-level constant.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[getSocketUrl called] --> B{NEXT_PUBLIC_SOCKET_URL set and non-empty?}
    B -- Yes --> C[Return explicit NEXT_PUBLIC_SOCKET_URL]
    B -- No --> D{Running in browser?\nwindow defined?}
    D -- No SSR --> E[Return http://localhost:3002]
    D -- Yes --> F{Is page origin a localhost hostname?\nlocalhost / 127.0.0.1 / ::1}
    F -- Yes --> E
    F -- No --> G[Return window.location.origin\ne.g. https://sim.example.com]

    style C fill:#4ade80
    style G fill:#4ade80
    style E fill:#facc15
Loading

Reviews (6): Last reviewed commit: "chore: untrack and ignore .claude/schedu..." | Re-trigger Greptile

Comment thread apps/sim/lib/core/utils/urls.ts Outdated
Comment thread packages/db/scripts/migrate-deployment-versions.ts
Comment thread apps/sim/lib/auth/auth.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 592b97c. Configure here.

Migration scripts now import generateId from @sim/utils/id; without copying packages/utils into the image, bun install fails to resolve the workspace dep at build time and the import fails at runtime.
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5420245. Configure here.

The realtime service never reads NEXT_PUBLIC_SOCKET_URL — its env schema
only includes BETTER_AUTH_URL, NEXT_PUBLIC_APP_URL, ALLOWED_ORIGINS,
BETTER_AUTH_SECRET, INTERNAL_API_SECRET, DATABASE_URL, and REDIS_URL.
Remove the dead config from all helm values files and the values schema.
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread helm/sim/values.schema.json
The default in values.yaml is now "" (empty string), which falls back to
the page origin at runtime. The schema previously required a valid URI,
which would reject the default. Mirror the INTERNAL_API_BASE_URL pattern
using anyOf with const "". Also add TRUSTED_ORIGINS to the schema.
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

The page-origin fallback in getSocketUrl() means self-hosters no longer
need to set NEXT_PUBLIC_SOCKET_URL when realtime is on the same origin
as the app. Update docs to reflect this:

- Remove NEXT_PUBLIC_SOCKET_URL from .env scaffolding examples in
  docker.mdx, platforms.mdx, environment-variables.mdx
- Mark the variable as Optional in the env vars table with the new
  default behavior described
- Update troubleshooting to point at reverse-proxy /socket.io routing
  rather than the env var
- Flip dev docker-compose defaults (local, ollama, devcontainer) from
  http://localhost:3002 to empty for consistency with prod.yml; the
  in-code localhost fallback handles the dev case identically

Applied across all 6 documentation languages (en/fr/de/ja/es/zh).
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c0de38b. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 7b32375. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant